package mspbots.next.ticket.core.grader;

import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.annotation.GradeRuleNotFoundException;
import mspbots.next.ticket.core.data.rules.Rule;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

/**
 * com.mspbots.core.ticketai.GradeRuleInterceptorRegistry
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/8/20
 */
@Log4j2
@Service
public class GradeRuleInterceptorRegistry {

    private final Map<String, GradeRuleInterceptor> sourceRuleInterceptorMap;
    private final AutowireCapableBeanFactory autowireCapableBeanFactory;

    public GradeRuleInterceptorRegistry(Map<String, GradeRuleInterceptor> ruleInterceptorMap,
                                        AutowireCapableBeanFactory autowireCapableBeanFactory) {
        this.sourceRuleInterceptorMap = ruleInterceptorMap;
        this.autowireCapableBeanFactory = autowireCapableBeanFactory;
    }

    public Mono<GradeRuleInterceptorRegistration> registryInterceptor(Rule rule) {
        return Mono.justOrEmpty(this.sourceRuleInterceptorMap.keySet().parallelStream()
                .filter(key -> key.equalsIgnoreCase(rule.getName())).findAny())
                .flatMap(this::newInstanceInterceptor)
                .flatMap(gradeRuleInterceptor -> this.prepareInterceptor(gradeRuleInterceptor, rule));
    }

    private Mono<GradeRuleInterceptor> newInstanceInterceptor(String key) {
        try {
            Class<? extends GradeRuleInterceptor> classRule = this.sourceRuleInterceptorMap.get(key)
                    .getClass();
            GradeRuleInterceptor ruleBean = classRule.getConstructor().newInstance();
            this.autowireCapableBeanFactory.autowireBean(ruleBean);
            return Mono.just(ruleBean);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            log.error("Rule [{}] get interceptor is error. msg: {}", key, e.getMessage());
            return Mono.error(new GradeRuleNotFoundException());
        }
    }

    private Mono<GradeRuleInterceptorRegistration> prepareInterceptor(GradeRuleInterceptor ruleInterceptor,
                                                                      Rule rule) {
        GradeRuleInterceptorRegistration registration = new GradeRuleInterceptorRegistration(rule.getName(),
                ruleInterceptor);

        return registration.prepare(rule)
                .onErrorResume(err -> {
                    log.error("Prepare rules [{}] error, msg: {}", rule.getName(), err.getMessage());
                    return Mono.empty();
                });
    }

}
